﻿using System;
using System.Collections.Generic;
using System.Linq;
using Swift;
using Swift.Math;
using SCM;
using RVO;

namespace SCM
{
    /// <summary>
    /// 战斗地图
    /// </summary>
    public class Map
    {
        // 所属房间
        public Room Room { get; set; }

        // 地图尺寸
        public Vec2 Size { get; private set; }
        public Vec2 HalfSize { get { return Size / 2; } }

        // 地图上的单位
        StableDictionary<string, Unit> units = new StableDictionary<string, Unit>();

        // RVO 模拟器处理碰撞躲避
        Simulator simulator = null;
        Simulator airforceSim = null; // 空中对象

        // RVOAgent 的 id 映射到 Unit 对象
        StableDictionary<int, Unit> rvoID2Units = new StableDictionary<int, Unit>();
        StableDictionary<int, Unit> rvoID2AirUnits = new StableDictionary<int, Unit>();

        // 地图格子缩小一点，牺牲精度，少占点内存
        public static int GridSizeScaler { get { return 10; } }

        public Map(Vec2 size, Fix64 frameInterval)
        {
            Size = size;
            grids = new MapGrid(Size, GridSizeScaler);
            CreateRVOSimulator(frameInterval);
        }

        public void Clear()
        {
            DestroyRVOSimulator();
        }

        public bool AddUnitAt(Unit u)
        {
            return u.cfg.IsAirForce ? AddUnitInAir(u) : AddUnitOnGround(u);
        }

        public void AddObstacle(IList<Vec2> pts)
        {
            // 定点数 > 2 的障碍就不是线段，需要自动封闭
            var ptCnt = pts.Count;
            var loopCnt = ptCnt > 2 ? ptCnt : ptCnt - 1;
            FC.For(loopCnt, (i) =>
            {
                var p1 = pts[i];
                var p2 = pts[i == ptCnt - 1 ? 0 : i + 1];
                grids.SetLine(p1.x, p1.y, p2.x, p2.y);
            });
            simulator.addObstacle(pts);
            simulator.processObstacles();
        }

        bool AddUnitInAir(Unit u)
        {
            units[u.UID] = u;

            if (u.cfg.RealSize > 0)
            {
                var aid = airforceSim.addAgent(u.Pos, u.cfg.RealSize, u.cfg.MaxVelocity);
                u.RVOAgentID = aid;
                rvoID2AirUnits[aid] = u;
            }
            else
                u.RVOAgentID = 0;

            return true;
        }

        bool AddUnitOnGround(Unit u)
        {
            if (!grids.CheckSpareSpace(u.Pos, u.cfg.RealSize))
                return false;

            units[u.UID] = u;

            if (u.cfg.RealSize > 0)
            {
                var aid = simulator.addAgent(u.Pos, u.cfg.RealSize, u.cfg.MaxVelocity);
                u.RVOAgentID = aid;
                rvoID2Units[aid] = u;

                if (u.cfg.MaxVelocity <= 0)
                    grids.SetGrid(u.Pos.x, u.Pos.y, u.cfg.RealSize);
            }
            else
                u.RVOAgentID = 0;

            return true;
        }

        public void RemoveUnit(string uid)
        {
            var u = units[uid];
            units.Remove(uid);

            if (u.RVOAgentID > 0)
            {
                var aid = u.RVOAgentID;

                if (u.cfg.IsAirForce)
                {
                    rvoID2AirUnits.Remove(aid);
                    airforceSim.removeAgent(aid);
                }
                else
                {
                    rvoID2Units.Remove(aid);
                    simulator.removeAgent(aid);

                    if (u.cfg.MaxVelocity <= 0)
                        grids.UnsetGrid(u.Pos.x, u.Pos.y, u.cfg.RealSize);
                }
            }
        }

        public Unit Get(string uid)
        {
            return units.ContainsKey(uid) ? units[uid] : null;
        }

        public Unit[] AllUnits
        {
            get
            {
                return units.ValueArray;
            }
        }

        #region 碰撞计算相关

        // 设置指定单位的行进速度
        public void SetPreferredVelocity(Unit u, Vec2 v)
        {
            var sim = u.cfg.IsAirForce ? airforceSim : simulator;
            sim.setAgentPrefVelocity(u.RVOAgentID, v);
        }

        public void DoOneStep(int te)
        {
            DoOneStepOnGround(te);
            DoOneStepInAir(te);
        }

        void DoOneStepInAir(int te)
        {
            foreach (var agentID in airforceSim.getAllAgents())
            {
                var u = rvoID2AirUnits[agentID];

                var pos = airforceSim.getAgentPosition(agentID);
                if (u.Pos != pos)
                    airforceSim.setAgentPosition(agentID, u.Pos);

                if (u.cfg.MaxVelocity > 0)
                    airforceSim.setAgentPrefVelocity(agentID, u.PreferredVelocity * 1000);
            }

            // 检查各单位向目标行进的情况，如果向同一目标行进的一组单位的中心不再靠近
            airforceSim.setTimeStep(te / 1000.0f);
            airforceSim.doStep();

            foreach (var agentID in airforceSim.getAllAgents())
            {
                var pos = airforceSim.getAgentPosition(agentID);
                var v = airforceSim.getAgentVelocity(agentID);
                var u = rvoID2AirUnits[agentID];

                if (pos != u.Pos)
                {
                    if (!pos.InRect(Size))
                        pos = pos.Clamp(Size);

                    u.Pos = pos;
                }

                if (v.Length > float.Epsilon)
                    u.Dir = v.Dir();
            }
        }

        // 推进游戏进度
        public void DoOneStepOnGround(int te)
        {
            foreach (var agentID in simulator.getAllAgents())
            {
                var u = rvoID2Units[agentID];

                var pos = simulator.getAgentPosition(agentID);
                if (u.Pos != pos)
                    simulator.setAgentPosition(agentID, u.Pos);

                if (u.cfg.MaxVelocity > 0)
                    simulator.setAgentPrefVelocity(agentID, u.PreferredVelocity * 1000);
            }

            // 检查各单位向目标行进的情况，如果向同一目标行进的一组单位的中心不再靠近
            simulator.setTimeStep(te / 1000.0f);
            simulator.doStep();

            foreach (var agentID in simulator.getAllAgents())
            {
                var pos = simulator.getAgentPosition(agentID);
                var v = simulator.getAgentVelocity(agentID);
                var u = rvoID2Units[agentID];

                if (pos != u.Pos)
                {
                    if (!pos.InRect(Size))
                        pos = pos.Clamp(Size);

                    u.Pos = pos;
                }

                if (v.Length > float.Epsilon)
                    u.Dir = v.Dir();
            }
        }

        void CreateRVOSimulator(Fix64 frameInterval)
        {
            simulator = new RVO.Simulator();
            simulator.setTimeStep(frameInterval);
            simulator.setAgentDefaults(50, 5, 1, 1, 0f, 0f, Vec2.Zero);
            simulator.processObstacles();

            airforceSim = new RVO.Simulator();
            airforceSim.setTimeStep(frameInterval);
            airforceSim.setAgentDefaults(50, 5, 1, 1, 0f, 0f, Vec2.Zero);
            airforceSim.processObstacles();
        }

        void DestroyRVOSimulator()
        {
            simulator.Clear();
            simulator = null;

            airforceSim.Clear();
            airforceSim = null;
        }

        #endregion

        #region 地图位置占用相关

        MapGrid grids = null;

        public bool CheckSpareSpace(Vec2 center, Fix64 radius) { return grids.CheckSpareSpace(center, radius); }
        public bool CheckSpareSpace(Fix64 cx, Fix64 cy, Fix64 radius) { return grids.CheckSpareSpace(cx, cy, radius); }
        public bool FindNearestSpareSpace(Vec2 center, Fix64 radius, Fix64 fromDistance, out Vec2 pt) { return grids.FindNearestSpareSpace(center, radius, fromDistance, out pt); }
        public int GetInGrid(Fix64 x, Fix64 y) { return grids.GetInGrid(x, y); }
        public void SetInGrid(Fix64 x, Fix64 y, int v) { grids.SetInGrid(x, y, v); }

        #endregion
    }
}
